#include "VxIrrLib.h"

/*
zoom = 70.710678
                  B
               /  |
 hypotenuse  /    | opposite
           /      |
         / Angle |
        A---------C
		 adjacent
In a right-angled triangle,
Sin= Opposite Side/Hypotenuse
Cos= Adjacent Side/Hypotenuse
Tan= SinAngle/CosAngle  = Opposite Side/Adjacent Side
CosecAngle = 1/SinAngle= Hypotenuse/Opposite Side
SecAngle = 1/CosAngle = Hypotenuse/Adjacent Side
CotAngle = 1/tanAngle = CosAngle/SinAngle = Adjacent Side/Opposite Side
SinAngleCosecAngle = CosAngleSecAngle = TanAngleCotAngle = 1
Sin(90-Angle) = CosAngle, Cos(90-Angle) = SinAngle
SinAngle + CosAngle = 1
TanAngle + 1 = SecAngle
CotAngle + 1 = CosecAngle
*/


//============================================================================
//=== constructor ===//
CameraBase::CameraBase( VxIrrBase * poIrrBase, S32 s32Id, S32 s32OwnerId )
	: m_poIrrBase(poIrrBase)
	, m_s32EntId( s32Id )				// id of this cam
	, m_s32OwnerId( s32OwnerId )	// owner of this cam
	, m_eCameraMode(eCameraModeOrbit)
	, m_poCamNode(0)
	, m_poSelectedNode(0)
	, m_iScnSizeX(0)			// screen width
	, m_iScnSizeY(0)			// screen height
	//=== orbit cam vars ===//
	, m_v3fDirection( 0.0f, 0.0f, 0.0f )
	, m_f32Yaw(0.0f)
	, m_f32Pitch(30.0f)
	, m_f32MaxDistance(5.0f)
	, m_f32CurDistance(5.0f)
	, m_bCamChanged(0)			// if true cam has changed direction or position
{
	m_poCamNode = m_poIrrBase->m_poSceneMgr->addCameraSceneNode( 0, irr::core::vector3df(-180,200,0), irr::core::vector3df(0,0,0), s32Id );
	m_poCamNode->setNearValue( 0.1f );
	m_poCamNode->setFarValue( 1000.0f );
	m_poIrrBase->m_poCam = this;
}
//============================================================================
//==== destructor ===//
CameraBase::~CameraBase()
{

}

//============================================================================
//=== implementation ===//
//============================================================================
//============================================================================
//!	create and startup camera
RCODE CameraBase::Startup( VxIrrBase * pgIrrBase )
{
	return 0;
}
//============================================================================
//!	shutdown camera
void CameraBase::Shutdown( void )
{

}
//============================================================================
//! set view as if viewing a doll from front side
void CameraBase::SetDollView(	irr::scene::ISceneNode *	poNode, 				// irrlicht scene node
								irr::f32					f32DollHeightOffs,		// height offset
								BOOL						bEnableDollRotate )		// if true then rotate selected node
{
	SetCameraMode( eCameraModeDoll );
	m_poSelectedNode = poNode;
	//m_f32DollHeightOffs = f32HeightOffs;
	//m_bEnableDollRotate = bEnableDollRotate;

	float f32DollHeight = GetModelHeight( poNode );

	irr::f32 f32Distance = f32DollHeight * 4;
	if( f32Distance < 10.0f )
	{
		f32Distance = 10.0f;
	}
	irr::f32 f32YLookAt = f32DollHeight / 2 + f32DollHeightOffs;
	irr::core::vector3df v3fViewCamPos( f32Distance, f32YLookAt, 0.0f );
	m_poCamNode->setPosition( v3fViewCamPos );

	irr::core::vector3df v3fViewCamTarget( 0.0f, f32YLookAt, 0.0f );
	m_poCamNode->setTarget( v3fViewCamTarget );
	//UpdateCamPosition();
	log_msg( 0, "CameraBase::SetDollView poNode=%d f32DollHeightOffs=%3.2f\n", poNode, f32DollHeightOffs );
}
//============================================================================
//! set size of screen
void CameraBase::SetScreenSize( int iScnSizeX, int iScnSizeY )
{
	m_iScnSizeX = iScnSizeX;			// screen width
	m_iScnSizeY = iScnSizeY;			// screen height
	//UpdateViewportAspect( iScnSizeX, iScnSizeY );
	log_msg( 0, "CameraBase::SetScreenSize w=%d h=%d\n", iScnSizeX, iScnSizeY );
}
//============================================================================
//! update aspect ratio
void CameraBase::UpdateViewportAspect( int sx, int sy)
{
   if( m_poCamNode )
   {
      // When the viewport is 440 pixels tall, we want a FOV of 70 degrees
      const float kfBaseFOVdeg = 70.0f;
      const float kfBaseHeight = 440.0f;
      const float kfBaseFOV    = irr::core::PI * 360.0f / kfBaseFOVdeg;

      float fBaseRatio = fabs( tan( kfBaseFOV ) / kfBaseHeight );

      float aspect = (float)sx / sy;
      float fov = atan( fBaseRatio * sy ) * 2.0f;

      m_poCamNode->setAspectRatio( aspect );
      m_poCamNode->setFOV( fov );

   }
}
//============================================================================
//! update obit camera rotation and pitch based on the mouse
void CameraBase::HandleMouseMotion( F32 f32X, F32 f32Y ) 
{
	m_f32Yaw += f32X;
	m_f32Pitch += f32Y;

	if( m_f32Pitch > 89.0f )
	{
		m_f32Pitch = 89.0f;
	}
	else if( m_f32Pitch <= -89.0f )
	{
		m_f32Pitch = -89.0f;
	}
}

//============================================================================
//! update obit camera target and position
void CameraBase::UpdateCamera( const irr::core::vector3df& v3fTarget )
{
	if( eCameraModeOrbit != GetCameraMode() )
	{
		// dont update orbit if not in orbit mode
		return;
	}
	
	// set transform with camera rotation
	m_mat4OrbitTransform.makeIdentity();
	m_mat4OrbitTransform.setRotationDegrees( irr::core::vector3df( m_f32Pitch, m_f32Yaw, 0.0f )); 

	// use normalize vector rotated to camera direction for sound listener direction
	irr::core::vector3df v3fDirection(0.0f, 0.0f, 1.0f);
	m_mat4OrbitTransform.transformVect( v3fDirection );

	// set listener direction
	g_oSndMgr.SetListenDirection( v3fDirection.X, v3fDirection.Y, v3fDirection.Z );

	// set current distance to target
	m_f32CurDistance = m_f32MaxDistance;
	// position the camera in given direction at current distance
	v3fDirection *= -m_f32CurDistance;

	// set target
	m_poCamNode->setTarget( v3fTarget );

	// calculate ray endpoints
	btVector3 vec3RayStart( v3fTarget.X, v3fTarget.Y, v3fTarget.Z);
	btVector3 vec3RayEnd( vec3RayStart );
	vec3RayEnd[0] += v3fDirection.X;
	vec3RayEnd[1] += v3fDirection.Y;
	vec3RayEnd[2] += v3fDirection.Z;
	
	// get point on wall where ray collides
	btVector3 vec3HitWallNormal;
	m_poIrrBase->m_poPhysBase->RaycastWorld( vec3RayStart, vec3RayEnd, vec3HitWallNormal );
	
	// shorten ray a little bit so cam is not touching wall
	btVector3 vec3NewOffset = vec3RayEnd - vec3RayStart;
	vec3NewOffset *= 0.9f;
	vec3RayEnd = vec3RayStart + vec3NewOffset;

	// set the new camera position
	m_poCamNode->setPosition( irr::core::vector3df( vec3RayEnd[0], vec3RayEnd[1], vec3RayEnd[2] ) );

	// need to know if camera moved
	if( ( m_v3fPreviousPosition != m_poCamNode->getPosition() ) || 
		( m_v3fPreviousLookAt != m_poCamNode->getTarget()) )
	{
		m_bCamChanged = true;
		OnCamMoved();
	}
	else
	{
		m_bCamChanged = false;
	}

	m_v3fPreviousPosition = m_poCamNode->getPosition();
	m_v3fPreviousLookAt = m_poCamNode->getTarget();

}
